package com.kob.backend.websocket;

import com.alibaba.fastjson2.JSONObject;
import com.kob.backend.pojo.Bot;
import com.kob.backend.pojo.Record;
import com.kob.backend.pojo.User;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

public class Game extends Thread {
    private final Integer rows;
    private final Integer cols;
    private final Integer inner_walls_count;
    private final int[][] g;//存地图
    private final Player playerA,playerB;//存一局对战中两名玩家的信息（起点，每一步指令）
    private Integer nextStepA = null; //null表示没有获取到两名玩家的下一步操作，0,1,2,3表示上下左右
    private Integer nextStepB = null;
    private ReentrantLock lock = new ReentrantLock();//不同线程的读写操作需要加锁
    private String status = "playing";//playing -> finished
    private String loser = "";//判断睡输  all:平局  A:A输  B:B输
    private final static int[] dx = {-1,0,1,0};
    private final static int[] dy = {0,1,0,-1};
    private final static String addBotUrl = "http://127.0.0.1:3002/bot/add/";

    public Game(Integer rows, Integer cols, Integer inner_walls_count, Integer idA, Bot botA, Integer idB,Bot botB){
        this.rows = rows;
        this.cols = cols;
        this.inner_walls_count = inner_walls_count;
        this.g = new int[rows][cols];
        Integer botIdA = -1,botIdB = -1;
        String botCodeA = "",botCodeB = "";
        if(botA != null){
            botIdA = botA.getId();
            botCodeA = botA.getContent();
        }
        if(botB != null){
            botIdB = botB.getId();
            botCodeB = botB.getContent();
        }
        playerA = new Player(idA,botIdA,botCodeA,rows - 2,1,new ArrayList<>());
        playerB = new Player(idB,botIdB,botCodeB,1,cols - 2,new ArrayList<>());
    }

    public Player getPlayerA() {
        return playerA;
    }

    public Player getPlayerB() {
        return playerB;
    }

    public void setNextStepA(Integer nextStepA) {
        lock.lock();
        try{
            this.nextStepA = nextStepA;
        }finally {
            lock.unlock();
        }
    }

    public void setNextStepB(Integer nextStepB) {
        lock.lock();
        try{
            this.nextStepB = nextStepB;
        }finally {
            lock.unlock();
        }
    }

    //    返回地图
    public int[][] getG() {
        return g;
    }
//    判断连通性
    private boolean check_connectivity(int sx,int sy,int tx,int ty){
        if(sx == tx && sy == ty) return true;
        g[sx][sy] = 1;

        for (int i = 0; i < 4; i++) {
            int x = sx + dx[i],y = sy + dy[i];
            if(x >= 0 && x < rows && y >= 0 && y < cols && g[x][y] == 0){
                if(check_connectivity(x,y,tx,ty)){
                    g[sx][sy] = 0;
                    return true;
                }
            }
        }
        g[sx][sy] = 0;
        return false;
    }
//    画地图
    private boolean draw(){
//        初始化地图
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                g[i][j] = 0;
            }
        }
//        给四周加墙
        for(int r = 0; r < rows; r++){
            g[r][0] = g[r][cols - 1] = 1;
        }
        for(int c = 0; c < cols; c++){
            g[0][c] = g[rows - 1][c] = 1;
        }

//        随机创建障碍物，数量：inner_walls_count
        Random random = new Random();
        for (int i = 0; i < inner_walls_count / 2; i++) {
            for (int j = 0; j < 1000; j++) {
                int r = random.nextInt(rows);
                int c = random.nextInt(cols);
                //两条蛇起点不创建障碍物
                if(g[r][c] == 1 || g[rows - 1 - r][cols - 1 -c] == 1)
                    continue;
                if(r == rows - 2 && c == 1 || r == 1 && c == cols -2)
                    continue;
//                两边中心对称创建障碍物
                g[r][c] = g[rows - 1 - r][cols - 1 - c ] = 1;
                break;
            }
        }
        return check_connectivity(rows - 2,1,1,cols - 2);
    }

    public void createMap(){
        for (int i = 0; i < 1000; i++) {
            if(draw())
                break;
        }
    }

    private String getInput(Player player){ //将当前的局面信息，编码成字符串
        Player me,you;
        if(playerA.getId().equals(player.getId())){
            me = playerA;
            you = playerB;
        }else{
            me = playerB;
            you = playerA;
        }
        return getMapString() + "#" + me.getSx() + "#" + me.getSy() + "#(" + me.getStepString() + ")#"
                + you.getSx() + "#" + you.getSy() + "#(" + you.getStepString() +")";
    }

    private void sendBotCode(Player player){  //判断是否为AI Bot
        if(player.getBotId().equals(-1)) return;
        MultiValueMap<String,String> data = new LinkedMultiValueMap<>();
        data.add("user_id",player.getId().toString());
        data.add("bot_code",player.getBotCode());
        data.add("input",getInput(player));
        WebSocketServer.restTemplate.postForObject(addBotUrl,data,String.class);
    }

    private boolean nextStep(){   //等待两名玩家的下一步操作
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

//        两名玩家每操作一步都要向botRunning系统发送请求，传递信息
        sendBotCode(playerA);
        sendBotCode(playerB);

        for (int i = 0; i < 50; i++) {
            try {
                Thread.sleep(100);
                lock.lock();
                try {
                   if(nextStepA != null && nextStepB != null){
                       playerA.getSteps().add(nextStepA);
                       playerB.getSteps().add(nextStepB);
                       return true;
                   }
                }finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private boolean check_valid(List<Cell> cellsA,List<Cell> cellsB){
        int size = cellsA.size();
        Cell cell = cellsA.get(size - 1);
        if(g[cell.x][cell.y] == 1) return false;

        for (int i = 0; i < size - 1; i++) {
            if(cellsA.get(i).x == cell.x && cellsA.get(i).y == cell.y)
                return false;
        }

        for (int i = 0; i < size - 1; i++) {
            if(cellsB.get(i).x == cell.x && cellsB.get(i).y == cell.y)
                return false;
        }

        return true;
    }

    private void judge(){//判断两名玩家下一步操作是否合法
        List<Cell> cellsA = playerA.getCells();
        List<Cell> cellsB = playerB.getCells();
        boolean validA = check_valid(cellsA, cellsB);
        boolean validB = check_valid(cellsB, cellsA);
        if(!validA || !validB){
            status = "finished";
            if(!validA && !validB){
                loser = "all";
            }else if(!validA){
                loser = "A";
            }else{
                loser = "B";
            }
        }
    }

    private void sendAllMessage(String message){
        if(WebSocketServer.users.get(playerA.getId()) != null)
            WebSocketServer.users.get(playerA.getId()).sendMessage(message);
        if(WebSocketServer.users.get(playerB.getId()) != null)
            WebSocketServer.users.get(playerB.getId()).sendMessage(message);
    }
    private void sendMove(){//向两个client端传递队友移动信息
        lock.lock();
        try {
            JSONObject resp = new JSONObject();
            resp.put("event","move");
            resp.put("a_direction",nextStepA);
            resp.put("b_direction",nextStepB);
            sendAllMessage(resp.toJSONString());
            nextStepA = nextStepB = null;
        }finally {
            lock.unlock();
        }

    }

    public String getMapString(){
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                res.append(g[i][j]);
            }
        }
        return res.toString();
    }

    private void updateUserRating(Player player,Integer rating){
        User user = WebSocketServer.userMapper.selectById(player.getId());
        user.setRating(rating);
        WebSocketServer.userMapper.updateById(user);
    }


    private void saveToDateBase(){//保存到数据库中
        //更新天梯积分
        Integer ratingA = WebSocketServer.userMapper.selectById(playerA.getId()).getRating();
        Integer ratingB = WebSocketServer.userMapper.selectById(playerB.getId()).getRating();
        if("A".equals(loser)){
            ratingA -= 5;
            ratingB += 10;
        }else if("B".equals(loser)){
            ratingB -= 5;
            ratingA += 10;
        }
        updateUserRating(playerA,ratingA);
        updateUserRating(playerB,ratingB);
        Record record = new Record(null,playerA.getId(),playerA.getSx(),playerA.getSy(),playerB.getId()
                                    ,playerB.getSx(),playerB.getSy(),playerA.getStepString(),
                                    playerB.getStepString(),getMapString(),loser,new Date());

        WebSocketServer.recordMapper.insert(record);
    }

    private void sendResult(){//向两个client端公布结果
        JSONObject resp = new JSONObject();
        resp.put("event","result");
        resp.put("loser",loser);
        saveToDateBase();
        sendAllMessage(resp.toJSONString());
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {//两名玩家最多的操作步数
            if(nextStep()){//是否获取到下一个操作
                judge();
                if(status.equals("playing")){
                    sendMove();
                }else{
                    sendResult();
                    break;
                }
            }else{
                status = "finished";
                lock.lock();
                try {
                    if(nextStepA == null && nextStepB == null){
                        loser = "all";
                    }else if(nextStepA == null){
                        loser = "A";
                    }else{
                        loser = "B";
                    }
                }finally {
                    lock.unlock();
                }
                sendResult();
                break;
            }

        }
    }
}
